INTRO | |
---|---|
Hi again everyone. This is my second effort at a tutorial, and I will make this very similar to the last one, but not quite so slow. Though it should be pretty easy to follow. Contact me for help at any time at karnak@apexmail.com. | |
INFO | |
The Proggie - | Duelist's Crackme #4. Download from The Crackme Website. |
The Protection - | This crackme has the standard name/serial protection. The serial is tested against a generated value that is calculated from the name. |
The Required Tools - | I used NuMega Softice, the debugger of all debuggers. You could use W32Dasm, but I don't recommend it. Also, as per usual, a brain and an open mind are required. |
The Greetz - | Many thanks to those who take the time to write the crackmes. Thanks to all people in #cracking4newbies and #win32asm. Greetz to StRAzOr, ACiD BuRN, Volatility, The Sandman, Fravia, +ORC, AlX, _Tribe and all other aspiring Crackerz! |
Other Info - | All S-ICE commands will be highlighted in RED. I will
possibly use the following abbreviations:
- WTF = What the fuck?! This tutorial will (like the last one) focus on the ultimate solution - the Keygen. I will show those who wish to patch where/when/how, but will go on to show how to write the keygen for this crackme. It is a relatively simple algo, but I think alot can be learned by Keygenning this baby (esp for new-newbies :)). |
THE ESSAY | |
Righto people, lets fire up the crackme and see what we have. As already said
we are dealing with a name/serial registration. Punch in your name and serial. For this
crackme I will be using karnak and 19781205. Lets set a breakpoint in SICE that
should let us enter the code at the point where the proggie gets the text from
the edit controls on the screen. So BPX hmemcpy, coz
thats pretty universal (I mean alot of apps use it). Go back to windows, and hit the
'Check' button. WHAT?! No break?? How come SICE didn't pop up? Hmm... guess this aint
using hmemcpy. So what do we do? Lets try an API call. To shorten this considerably, I'll
just tell you that GetDlgItemText/GetDlgItemTextA don't work, neither do the
SendMessage/SendMessageA routines, or even GetWindowText/GetWindowTextA.
The actual routine that this program uses is the
SendDlgItemMessageA. So lets BPX SendDlgItemMessageA, and go
and press that 'Check' button once again. BINGO!! Now we're talkin! Righto, press F11 to return from the function call and well have a look at some of the code. This is what I could see: |
|
:00401127 6A00 PUSH 00 :00401129 6A00 PUSH 00 :0040112B 6A0E PUSH 0E :0040112D 6A03 PUSH 03 :0040112F FF7508 PUSH DWORD PTR [EBP+08] :00401132 E841020000 CALL USER32!SendDlgItemMessageA <--- Function call :00401137 A3AF214000 MOV [004021AF],EAX <--- We are here :0040113C 83F800 CMP EAX,00 :0040113F 0F84D5000000 JZ 0040121A :00401145 83F808 CMP EAX,08 :00401148 0F8FCC000000 JG 0040121A :0040114E 8BF0 MOV ESI,EAX :00401150 6A00 PUSH 00 :00401152 6A00 PUSH 00 :00401154 6A0E PUSH 0E :00401156 6A04 PUSH 04 :00401158 FF7508 PUSH DWORD PTR [EBP+08] :0040115B E818020000 CALL USER32!SendDlgItemMessageA <--- Function call :00401160 83F800 CMP EAX,00 :00401163 0F84B1000000 JZ 0040121A :00401169 3BF0 CMP ESI,EAX :0040116B 0F85A9000000 JNZ 0040121A :00401171 6860214000 PUSH 00402160 :00401176 6A08 PUSH 08 :00401178 6A0D PUSH 0D :0040117A 6A03 PUSH 03 :0040117C FF7508 PUSH DWORD PTR [EBP+08] :0040117F E8F4010000 CALL USER32!SendDlgItemMessageA <--- Function call :00401184 6879214000 PUSH 00402179 :00401189 6A10 PUSH 10 :0040118B 6A0D PUSH 0D :0040118D 6A04 PUSH 04 :0040118F FF7508 PUSH DWORD PTR [EBP+08] :00401192 E8E1010000 CALL USER32!SendDlgItemMessageA <--- Function call |
|
OK we can see here that we are calling this function 4 times. Thats interesting, since we only have 2 controls. Anyway take a look at the first function call. One of the paramters to this call is 0E. This is the equivalent to the WM_GETTEXTLENGTH message in windows. So here we are finding the length of the text in one of the controls. Which one? Well, the return value is in EAX, and my contains 00000006 which tells me that it is referring to the name. We are then comparing this to 0 and 8. So in other words, we are jumping to the message box routine (ie 0040121A) if the length of the text is less that one, or greater than 8. After this, we then use the same function with the same 0E parameter to get the length of the serial number. This length is compared to the length of the name, if they aren't the same, then piss off. So at this point we'll have to go and shorten the serial number to make this loop work for us. I shortened mine to 197812. OK, now we've done the first couple of tests to make sure the name/serial combo COULD be ok, we then use the same routine, this time with the 0D parameter, which is the same as the WM_GETTEXT message. So we are storing the 2 values in memory somewhere, and continue on to the next peice of code, which looks like this: | |
:00401197 B9FFFFFFFF MOV ECX,FFFFFFFF :0040119C 41 INC ECX :0040119D 0FBE8160214000 MOVSX EAX,BYTE PTR [ECX+00402160] :004011A4 83F800 CMP EAX,00 :004011A7 7432 JZ 004011DB :004011A9 BEFFFFFFFF MOV ESI,FFFFFFFF :004011AE 83F841 CMP EAX,41 :004011B1 7C67 JL 0040121A :004011B3 83F87A CMP EAX,7A :004011B6 7762 JA 0040121A :004011B8 83F85A CMP EAX,5A :004011BB 7C03 JL 004011C0 :004011BD 83E820 SUB EAX,20 :004011C0 46 INC ESI :004011C1 0FBE9617204000 MOVSX EDX,BYTE PTR [ESI+00402017] :004011C8 3BC2 CMP EAX,EDX :004011CA 75F4 JNZ 004011C0 :004011CC 0FBE863C204000 MOVSX EAX,BYTE PTR [ESI+0040203C] :004011D3 898194214000 MOV [ECX+00402194],EAX :004011D9 EBC1 JMP 0040119C :004011DB FF35AF214000 PUSH DWORD PTR [004021AF] :004011E1 6894214000 PUSH 00402194 :004011E6 6879214000 PUSH 00402179 :004011EB E854000000 CALL 00401244 <--- Intersting :004011F0 83F801 CMP EAX,01 :004011F3 0F84DEFEFFFF JZ 004010D7 :004011F9 EB1F JMP 0040121A |
|
OK, after stepping over the first few lines of code, we can see that the
proggie is checking to see if the characters that have been entered into the name field
are valid characters. If they are, it converts them to uppercase (letter = letter - 20h)
if they are lowercase. OK, after doing that, we seem to be comparing each letter of our
name to some secret magic letter. At this point, to find out what we are comparing things
to type DD 00402017. You should get a nice long string in your
data window looking like this: A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9 So whats going on? Well, for each character in our name we're looping through this string to find a match for the letter. So my first match is at the 5th letter 'K'. This number is then used in another magic string. To have a look at this magic string type DD 0040203C, and you will see this: SU7CSJKF09NCSDO0SDF09SDRLVK7809S4NF So the 5th charactor of this string is 'S'. That is the first value I get. OK, we are looping through the whole name, so after the loop, my string looks like this: SS90SS Hmm, this is interesting... could this be my serial number? Well, lets have a look. After the loop has finished, and we have our string, we CALL 00401244, and then test a value. If this value is 1 then we have a valid key and we go somewhere, if its not 1 we go to the same 'BAD' messagebox at 0040121A. So after this function call we want EAX to be equal to 01h. Lets check out the code in the function call. It looks like this: |
|
:00401244 C8000000 ENTER 0000,00 :00401248 B801000000 MOV EAX,00000001 :0040124D 8B7D08 MOV EDI,[EBP+08] :00401250 8B750C MOV ESI,[EBP+0C] :00401253 8B4D10 MOV ECX,[EBP+10] :00401256 F3A6 REPZ CMPSB :00401258 67E305 JCXZ 00401260 :0040125B B800000000 MOV EAX,00000000 :00401260 C9 LEAVE :00401261 C20C00 RET 000C |
|
This is just a comparing function! We loop through the string we've created
test each value with the corresponding value of the serial. At the start of the function
we set EAX to 1. We want it to stay this way for the test after the function. So if the
serial and the generated string is the same, the we leave, otherwise we set EAX to 0
and then leave. So lets go back to our app and punch in our serial that the program generated for us, in my case its SS90SS. Well done! Program registered! |
|
THE KEYGEN | |
Easy keygen this one. Just create an algo that will receive as input a 1 - 8
character name, convert the name to UPPERcase, and use the 2 magic strings as a map to
get the generated value. Pretty damn easy!! Here is a PSUEDO-like example:
GetName(); |
|
THE END | |
Well thats it guys, hope you learned something from this essay! Please drop me a line at karnak@apexmail.com if you need any help, or would like to see the source to my keygen. |